package org.termine.jsf;

import java.io.Serializable;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.model.ListDataModel;

import org.primefaces.model.SelectableDataModel;
import org.termine.entity.Availability;
import org.termine.logic.AvailabilityManager;

@ManagedBean
@ViewScoped
public class AvailabilityJSF extends BaseJSF implements Serializable {

	private static final long serialVersionUID = -4085480046224193090L;

	@ManagedProperty(value = "#{availabilityManager}")
	private AvailabilityManager availabilityManager;
	@ManagedProperty(value = "#{loginJSF}")
	private LoginJSF loginJSF;

	private AvailabilityDataModel availabilityDataModel;
	private Availability selectedAvailability;
	private Availability newAvailability;

	/*
	 * Init
	 */
	@PostConstruct
	public void init() {
		availabilityDataModel = new AvailabilityDataModel(getAvailabilityManager()
				.getAvailabilitiesForProvider(getLoginJSF().getPersonalData().getProvider_id()));
		newAvailability = new Availability();
		newAvailability.setType(Availability.AVAILABILITY);
	}

	/*
	 * Jsf Action Methods
	 */
	/*
	 * a new availability - may NOT overlap with existing availabilities - is
	 * only valid if start < end and start > now
	 * 
	 * a new unavailability - is only valid if start < end and start > now - may
	 * not overlap with an existing appointment
	 */
	public String addAvailability() {
		String result = getAvailabilityManager().addAvailability(newAvailability, getLoginJSF().getPersonalData().getProvider_id());
		if (result.equals("SUCCESS")) {
			init();
			setInfoMessage("The (un)availability has been added successfully!");
			return "SUCCESS";
		} else if (result.equals("INVALID")) {
			setErrorMessage("The (un)availability could not be saved! The end must be after the start. Start must not lie in the past.");
			return "ERROR";
		} else if (result.equals("OVERLAPS_WITH_AVAILABILITY")) {
			setErrorMessage("The availability could not be saved! It overlaps with an existing availability.");
			return "ERROR";
		} else if (result.equals("OVERLAPS_WITH_APPOINTMENT")) {
			setErrorMessage("The unavailability could not be saved! It overlaps with an existing appointment.");
			return "ERROR";
		} else {
			setFatalMessage("The (un)availability could not be saved! An error occurred.");
			return "ERROR";
		}
	}

	/*
	 * the updated availability - may NOT overlap with existing availabilities -
	 * is only valid if start < end
	 * 
	 * the updated unavailability - is only valid if start < end
	 * 
	 * the update is only valid, if no appointment ends up without it's
	 * corresponding availability
	 */
	public String updateAvailability() {
		String result = getAvailabilityManager().updateAvailability(selectedAvailability,
				getLoginJSF().getPersonalData().getProvider_id());
		if (result.equals("SUCCESS")) {
			init();
			setInfoMessage("The (un)availability has been updated successfully!");
			return "SUCCESS";
		} else if (result.equals("INVALID")) {
			setErrorMessage("The (un)availability could not be saved! The end must be after the start.");
			return "ERROR";
		} else if (result.equals("OVERLAPS_WITH_AVAILABILITY")) {
			setErrorMessage("The availability could not be saved! It overlaps with an existing availability.");
			return "ERROR";
		} else if (result.equals("OVERLAPS_WITH_APPOINTMENT")) {
			setErrorMessage("The unavailability could not be saved! It overlaps with an existing appointment.");
			return "ERROR";
		} else if (result.equals("APPOINTMENT_LOST")) {
			setErrorMessage("The availability could not be saved! An appointment would be lost.");
			return "ERROR";
		} else {
			setFatalMessage("The (un)availability could not be saved! An error occurred.");
			return "ERROR";
		}
	}

	/*
	 * an availability may be deleted, if no appointment ends up without it's
	 * corresponding availability an unavailability may always be deleted
	 */
	public String deleteAvailability() {
		String result = getAvailabilityManager().deleteAvailability(selectedAvailability);
		if (result.equals("SUCCESS")) {
			init();
			setInfoMessage("The (un)availability has been deleted successfully!");
			return "SUCCESS";
		} else if (result.equals("APPOINTMENT_LOST")) {
			setErrorMessage("The availability could not be deleted! An appointment would be lost.");
			return "ERROR";
		} else {
			setFatalMessage("The (un)availability could not be deleted! An error occurred.");
			return "ERROR";
		}
	}

	/*
	 * TableModel
	 */
	public class AvailabilityDataModel extends ListDataModel<Availability> implements
			SelectableDataModel<Availability> {

		public AvailabilityDataModel() {
		}

		public AvailabilityDataModel(List<Availability> data) {
			super(data);
		}

		public Availability getRowData(String rowKey) {
			@SuppressWarnings("unchecked")
			List<Availability> availabilities = (List<Availability>) getWrappedData();

			for (Availability availability : availabilities) {
				if (availability.getAvailability_id().equals(Integer.parseInt(rowKey))) {
					return availability;
				}
			}
			return null;
		}

		public Object getRowKey(Availability availability) {
			return availability.getAvailability_id();
		}

	}

	/*
	 * Getters & Setters
	 */
	public AvailabilityDataModel getAvailabilityDataModel() {
		return availabilityDataModel;
	}

	public void setAvailabilityDataModel(AvailabilityDataModel availabilityDataModel) {
		this.availabilityDataModel = availabilityDataModel;
	}

	public Availability getSelectedAvailability() {
		return selectedAvailability;
	}

	public void setSelectedAvailability(Availability selectedAvailability) {
		this.selectedAvailability = selectedAvailability;
	}

	public Availability getNewAvailability() {
		return newAvailability;
	}

	public void setNewAvailability(Availability newAvailability) {
		this.newAvailability = newAvailability;
	}

	public AvailabilityManager getAvailabilityManager() {
		return availabilityManager;
	}

	public void setAvailabilityManager(AvailabilityManager availabilityManager) {
		this.availabilityManager = availabilityManager;
	}

	public LoginJSF getLoginJSF() {
		return loginJSF;
	}

	public void setLoginJSF(LoginJSF loginJSF) {
		this.loginJSF = loginJSF;
	}

}
